﻿using System;
using System.Collections.Generic;
using JetBrains.Annotations;
using JetBrains.ProjectModel;
using JetBrains.ReSharper.Psi;
using JetBrains.ReSharper.Psi.ExtensionsAPI.Tree;
using JetBrains.ReSharper.Psi.GeneratedCode;
using JetBrains.ReSharper.Psi.Resolve;
using JetBrains.ReSharper.Psi.Tree;

namespace GammaJul.ReSharper.GammaXaml.Psi {
	
	/// <summary>
	/// Contains extension methods related to PSI.
	/// </summary>
	public static class PsiExtensions {

		/// <summary>
		/// Deletes a tree node.
		/// </summary>
		/// <param name="treeNode">The tree node to delete.</param>
		public static void Delete([CanBeNull] this ITreeNode treeNode) {
			if (treeNode != null)
				ModificationUtil.DeleteChild(treeNode);
		}

		/// <summary>
		/// Replaces a tree node by another node.
		/// </summary>
		/// <typeparam name="TTreeNode">The type of the replaced node.</typeparam>
		/// <param name="oldNode">The node to replace.</param>
		/// <param name="newNode">The node that is replacing <paramref name="oldNode"/>.</param>
		/// <returns>The resulting tree node that replaced <paramref name="oldNode"/>.</returns>
		/// <remarks><paramref name="newNode"/> should not be child of <paramref name="oldNode"/>.</remarks>
		[NotNull]
		public static TTreeNode ReplaceBy<TTreeNode>([NotNull] this ITreeNode oldNode, [NotNull] TTreeNode newNode)
		where TTreeNode : ITreeNode {
			return ModificationUtil.ReplaceChild(oldNode, newNode);
		}

		/// <summary>
		/// Finds the first node in a collection that isn't in a generated file. If there is only node in the collection
		/// and that node is present in a generated file, it is returned even though it's generated.
		/// </summary>
		/// <typeparam name="T">The node type.</typeparam>
		/// <param name="source">The node collection.</param>
		/// <returns>A <see cref="ITreeNode"/>, or a default value if the collection is empty.</returns>
		public static T FirstInNonGeneratedFileOrFallback<T>([NotNull] this IEnumerable<T> source)
		where T : ITreeNode {
			if (source == null)
				throw new ArgumentNullException("source");

			T generated = default(T);
			foreach (T item in source) {
				IProjectFile projectFile = item.GetSourceFile().ToProjectFile();
				if (projectFile == null || projectFile.IsGeneratedFile())
					generated = item;
				else
					return item;
			}
			return generated;
		}

		[CanBeNull]
		public static ITypeElement ResolvedType([CanBeNull] this IReference reference) {
			return reference == null
				? null
				: reference.Resolve().DeclaredElement as ITypeElement;
		}

	}

}